package xstring

import (
	"encoding/json"
	"strings"
	"time"
)

// JSON映射转化
// out.key支持使用函数进行其他处理，使用|进行拼接，所以函数参数中禁止出现 `:` 和 `|`
//
// json - 将 json 字符串转化成 json 对象
// date - 将 value 的值转化成 yyyy-mm-dd 格式的日期
// time - 将 value 的值转化成 hh:mm:ss 格式的时间
// datetime - 将 value 的值转化成 yyyy-mm-dd hh:mm:ss 格式的日期时间
// unix - 将 value 的值转化成 UNIX 时间戳(支持+xx:xx格式时区调整，如+[0]8[:00]会计算为-time.Hour*8)
// udt - 将unix时间戳转化为UTC时间，并格式化为 yyyy-mm-dd hh:mm:ss 格式的日期时间(支持使用 2006-01-02 15:04:05 类似字样来调整输出格式)
// rep - 替换 value 中的字符串的值，使用格式要求为： rep:<查找字符>:<替换字符>
// spl - 直接根据特定字符串进行分割，使用格式要求为： spl:<切分字符>
// up - 字符转换为大写
// up1 - 字符串首字母大写
// low - 字符串转换为小写
// int - 转 int64 类型
// trim - 去除两侧空格
//
// rep - 高级用法：rep::A_{}_B    在字符串前后分别添加A_和_B({}指原字符串)
// spl - 高级用法：spl:{}    使用 : 分割字符（使用 {} 替换被占用的:）
//
//	in	输入的结构体类型，允许使用[]
//	out	输出的结构体类型，允许使用[]
//	mps	映射字段,格式为：in.key => out.key
//	ret	未加入 mps 的字段是删除还是保留 true-保留 false-舍弃
func J2jMap(in, out any, mps map[string]string, ret bool) error {
	ints := []map[string]any{}
	err := Any2any(in, &ints)
	if err != nil {
		// 输入的是map
		return Any2any(_J2jMap_a2a(in, mps, ret), out)
	}
	// 输入的是[]map
	tmp_outs := []map[string]any{}
	for i := 0; i < len(ints); i++ {
		tmp_outs = append(tmp_outs, _J2jMap_a2a(ints[i], mps, ret))
	}
	return Any2any(tmp_outs, out)
}

// 将输入的 map 转化为待输出的 map
//
//	in	待匹配格式化的 map
//	mps	映射列表，规则详见 `J2jMap` 函数
//	ret	未加入 mps 的字段是删除还是保留 true-保留 false-舍弃
func _J2jMap_a2a(in any, mps map[string]string, ret bool) map[string]any {
	tmp_out := map[string]any{}
	tmp_in := map[string]any{}
	Any2any(in, &tmp_in)
	for k, v := range tmp_in {
		if vs, ok := mps[k]; ok {
			if strings.Contains(vs, "|") {
				// 使用函数进行处理
				spl := strings.Split(vs, "|")
				for i := 1; i < len(spl); i++ {
					spl_maps := strings.Split(spl[i], ":")
					switch strings.ToLower(spl_maps[0]) {
					case "int":
						v = Any2Integer(v)
					case "low":
						v = strings.ToLower(AString(v))
					case "trim":
						v = strings.TrimSpace(AString(v))
					case "up1":
						v = strings.Title(AString(v))
					case "up":
						v = strings.ToUpper(AString(v))
					case "spl":
						if len(spl_maps) != 2 {
							continue
						}
						if spl_maps[1] == "{}" {
							spl_maps[1] = ":"
						}
						switch b := v.(type) {
						case []string:
							v = strings.Split(strings.Join(b, spl_maps[1]), spl_maps[1])
						default:
							v = strings.Split(AString(v), spl_maps[1])
						}
					case "rep":
						if len(spl_maps) != 3 {
							continue
						}
						if spl_maps[1] != "" {
							// 字符串替换
							v = strings.ReplaceAll(AString(v), spl_maps[1], spl_maps[2])
						} else {
							// 字符串填充
							v = strings.ReplaceAll(spl_maps[2], "{}", AString(v))
						}
					case "unix":
						if ts := ToTime(AString(v)).Unix(); ts > 0 {
							if len(spl_maps) == 2 {
								// 还有时区需要调整
								tzs := int64(0)
								spl := strings.Split(spl_maps[1], ":")
								if len(spl) == 1 {
									// 仅有小时的单位
									if strings.HasPrefix(spl[0], "-") {
										// -x时区
										tzs = ToInt64(spl[0][1:]) * 60 * 60
									} else if strings.HasPrefix(spl[0], "+") {
										// +x时区
										tzs = -ToInt64(spl[0][1:]) * 60 * 60
									} else {
										tzs = -ToInt64(spl[0]) * 60 * 60
									}
								} else if len(spl) >= 2 {
									// 有小时和分钟的单位
									if strings.HasPrefix(spl[0], "-") {
										// -x时区
										tzs = ToInt64(spl[0][1:]) * 60 * 60
										tzs += ToInt64(spl[1]) * 60
									} else if strings.HasPrefix(spl[0], "+") {
										// +x时区
										tzs = -ToInt64(spl[0][1:]) * 60 * 60
										tzs -= ToInt64(spl[1]) * 60
									} else {
										tzs = -ToInt64(spl[0]) * 60 * 60
										tzs -= ToInt64(spl[1]) * 60
									}
								}
								ts += tzs
							}
							v = ts
						}
					case "udt":
						format := time.DateTime
						if len(spl_maps) >= 2 {
							format = strings.Join(spl_maps[1:], ":")
						}
						v = time.Unix(Any2Integer(v), 0).Format(format)
					case "datetime":
						v = ToTime(AString(v)).Format(time.DateTime)
					case "time":
						v = ToTime(AString(v)).Format(time.TimeOnly)
					case "date":
						v = ToTime(AString(v)).Format(time.DateOnly)
					case "json":
						vt := AString(v)
						if !strings.Contains(vt, "\"") {
							vt = strings.ReplaceAll(vt, "'", "\"")
						}
						if vt == "" {
							v = nil
						} else if vt[0] == '[' {
							// 如果是数组
							tmp := []any{}
							json.Unmarshal([]byte(vt), &tmp)
							v = tmp
						} else if vt[0] == '{' {
							tmp := map[string]any{}
							json.Unmarshal([]byte(vt), &tmp)
							v = tmp
						}
					}
				}
				tmp_out[spl[0]] = v
			} else {
				tmp_out[vs] = v
			}
		} else if ret {
			tmp_out[k] = v
		}
	}
	return tmp_out
}
